/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package be.ulb.f203.projet.vues;

import be.ulb.f203.projet.metier.Arc;
import be.ulb.f203.projet.metier.Chemin;
import be.ulb.f203.projet.metier.DijkstraResult;
import be.ulb.f203.projet.metier.Graphe;
import be.ulb.f203.projet.metier.Noeud;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComponent;

/**
 *
 * @author Lam
 */
public class GrapheGui extends JComponent {

    private Graphe graphe;
    private boolean displayAllNodePath;
    private Noeud destNode;

    public GrapheGui(Graphe g) {
        graphe = g;
        displayAllNodePath = true;
        setPreferredSize(new Dimension(500, 500));
    }

    public Noeud getDestNode() {
        return destNode;
    }

    public void setDestNode(Noeud destNode) {
        this.destNode = destNode;
    }

    public boolean isDisplayAllNodePath() {
        return displayAllNodePath;
    }

    public void setDisplayAllNodePath(boolean displayAllNodePath) {
        this.displayAllNodePath = displayAllNodePath;
    }

    public Graphe getGraphe() {
        return graphe;
    }

    public void setGraphe(Graphe graphe) {
        this.graphe = graphe;
    }

    @Override
    public void paint(Graphics grphcs) {
        if (graphe == null) {
            return;
        }
        super.paint(grphcs);
        Graphics2D g2D = (Graphics2D) grphcs;
        enableHQRendering(g2D);
        g2D.setFont(new Font("Dialog", Font.BOLD, 13));
        List<Arc> arcs = graphe.getArcs();
        drawArcs(g2D, arcs, Color.BLACK, true);
        if (displayAllNodePath) {
            tracePlusCourtChemin(g2D);
        } else {
            tracePlusCourtCheminVers(g2D);
        }
        drawNodes(g2D);
    }

    private void tracePlusCourtCheminVers(Graphics2D g2D) {
        if (graphe.getRacine().equals(destNode)) {
            return;
        }
        DijkstraResult result = graphe.getPlusCourtsChemins();
        Noeud[] nodes = result.getPrec();
        int index = graphe.getNoeuds().indexOf(destNode);

        List<Arc> arcs = new ArrayList<>();
        Noeud currentNode = destNode;
        boolean ok = graphe.getArc(currentNode, nodes[index]) != null;
        while (ok) {
            arcs.add(graphe.getArc(currentNode, nodes[index]));
            currentNode = nodes[index];
            ok = !currentNode.equals(graphe.getRacine());
            index = graphe.getNoeuds().indexOf(nodes[index]);
        }
        drawArcs(g2D, arcs, Color.ORANGE, false);
    }

    private void tracePlusCourtChemin(Graphics2D g2D) {
        Chemin plusCourtChemin = graphe.getPlusCourtCheminAllNodes();
        if (plusCourtChemin != null) {
            drawArcs(g2D, plusCourtChemin.getArcs(), Color.ORANGE, false);
        }
    }

    private void drawArcs(Graphics2D g2D, List<Arc> arcs, Color color, boolean avecPoids) {
        int width = this.getWidth() - 40;
        int height = this.getHeight() - 40;
        int nbNoeuds = graphe.getOrder();
        double degrees = 360.0 / (double) nbNoeuds;

        List<Noeud> noeuds = graphe.getNoeuds();
        int nbArcs = arcs.size();
        g2D.setColor(color);
        g2D.setStroke(new BasicStroke(3F));
        for (int i = 0; i < nbArcs; i++) {
            Noeud depart = arcs.get(i).getDepart();
            Noeud arrivee = arcs.get(i).getArrivee();

            int posDepart = noeuds.indexOf(depart);
            int posArrivee = noeuds.indexOf(arrivee);

            float to_radian = (float) ((Math.PI * degrees * posDepart) / 180d);
            int yDep = (int) (Math.sin(to_radian) * (height / 2));
            int xDep = (int) (Math.cos(to_radian) * (width / 2));
            xDep += width / 2;
            yDep += height / 2;
            to_radian = (float) ((Math.PI * degrees * posArrivee) / 180d);
            int yArr = (int) (Math.sin(to_radian) * (height / 2));
            int xArr = (int) (Math.cos(to_radian) * (width / 2));
            xArr += width / 2;
            yArr += height / 2;
            g2D.drawLine(xDep, yDep, xArr, yArr);
            if (avecPoids) {
                int textWidth = g2D.getFontMetrics().stringWidth("" + arcs.get(i).getPoids());
                int textHeight = g2D.getFontMetrics().getHeight();
                g2D.setColor(Color.WHITE);
                g2D.fillRoundRect(Math.abs((xArr + xDep) / 2), Math.abs((yArr + yDep) / 2) - textHeight + 2, textWidth, textHeight, 4, 4);
                g2D.setColor(Color.BLACK);
                g2D.drawString("" + arcs.get(i).getPoids(), Math.abs((xArr + xDep) / 2), Math.abs((yArr + yDep) / 2));
            }
        }
    }

    private void drawNodes(Graphics2D g2D) {
        int width = this.getWidth() - 40;
        int height = this.getHeight() - 40;
        int nbNoeuds = graphe.getOrder();
        double degrees = 360.0 / (double) nbNoeuds;
        double current_angle = 0;
        List<Noeud> noeuds = graphe.getNoeuds();
        for (int i = 0; i < nbNoeuds; i++) {
            String nodeValue = noeuds.get(i).getValue().toString();
            int textWidth = g2D.getFontMetrics().stringWidth(nodeValue);
            float to_radian = (float) ((Math.PI * current_angle) / 180d);
            int y = (int) (Math.sin(to_radian) * (height / 2));
            int x = (int) (Math.cos(to_radian) * (width / 2));
            x += width / 2 - textWidth / 2 - 5;
            y += height / 2 - 5;

            current_angle += degrees;
            g2D.setColor(Color.BLACK);
            g2D.fillRoundRect(x, y, textWidth + 10, 30, 10, 10);
            g2D.setColor(Color.WHITE);
            g2D.drawString(nodeValue, x + 5, y + 17);
        }

    }

    private void enableHQRendering(Graphics2D aG) {
        aG.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        aG.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        aG.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        aG.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
    }
}
